package by.dvrudenko.avtobaza.dataaccess.impl;

import java.util.List;

import javax.inject.Inject;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.commons.lang3.Validate;
import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.dvrudenko.avtobaza.dataaccess.RouteDao;
import by.dvrudenko.avtobaza.datamodel.Car;
import by.dvrudenko.avtobaza.datamodel.Car_;
import by.dvrudenko.avtobaza.datamodel.Driver_;
import by.dvrudenko.avtobaza.datamodel.Route;
import by.dvrudenko.avtobaza.datamodel.Route_;
import by.dvrudenko.avtobaza.datamodel.UserOrder_;
import by.dvrudenko.avtobaza.datamodel.enums.OrderStatus;


@Repository
public class RouteDaoImpl extends AbstractDaoImpl<Long, Route> implements RouteDao {
	
	public RouteDaoImpl() {
		super(Route.class);
	}
	
	
	@Override // к-во записей по переданному id, если id не передается
	// считается все записи
	public Long getCount(Long id) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Long> criteria = cBuilder.createQuery(Long.class);
		Root<Route> root = criteria.from(Route.class);
		
		/* исправить передается id usera а поиск среди driver
		 * если передается id, то будут выбраны все водители передынным id
		 */
		if (id!=null){
			criteria.where(cBuilder.equal(root.get(Route_.driver), id));
		}

		criteria.select(cBuilder.count(root));

		TypedQuery<Long> query = getEm().createQuery(criteria);
		return query.getSingleResult();
	}

	@Override
	public List<Route> getAllRoute() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
	    CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
	    Root<Route> root = criteria.from(Route.class);

	    criteria.select(root);
	    root = prepareRoot(root);  
	    criteria.distinct(true);

	    TypedQuery<Route> query = getEm().createQuery(criteria);
	    List<Route> results = query.getResultList();
		return results;
	}

	@Override // посомотреть более внимально fetch тянуться лишние поля
	public List<Route> getAllRouteActive(Boolean condition) {
		Validate.notNull(condition, "condition - attributes can't be null");
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
	    CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
	    Root<Route> root = criteria.from(Route.class);
	    
	    criteria.select(root);
	    
	    root.fetch(Route_.userOrder).fetch(UserOrder_.user);
	    root.fetch(Route_.userOrder).fetch(UserOrder_.cargoBeginningCity);
	    root.fetch(Route_.userOrder).fetch(UserOrder_.cargoFinalCity);
		criteria.where(cBuilder.equal(root.get(Route_.active), condition));
	    criteria.distinct(true);

	    TypedQuery<Route> query = getEm().createQuery(criteria);
	    List<Route> results = query.getResultList();
		return results;
	}

	@Override // все активные задания по OrderStatus
	public List<Route> getAllActiveRouteByStatus(OrderStatus orderStatus) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
	    CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
	    Root<Route> root = criteria.from(Route.class);
	    criteria.select(root);    
	    root = prepareRoot(root);    
		criteria.where(cBuilder.and(cBuilder.equal(root.get(Route_.active), true),
				(cBuilder.equal(root.get(Route_.userOrder).get(UserOrder_.orderStatus), orderStatus))));
	
	    criteria.distinct(true);

	    TypedQuery<Route> query = getEm().createQuery(criteria);
	    List<Route> results = query.getResultList();
		return results;
	}
	
	@Override //  достает один маршрут по id
	public Route getSingleRouteByUserOrderId(Long id) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
	    CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
	    Root<Route> root = criteria.from(Route.class);
	    criteria.select(root);    
	    root = prepareRoot(root);
		criteria.where(cBuilder.equal(root.get(Route_.userOrder), id));

	    TypedQuery<Route> query = getEm().createQuery(criteria);
	    Route results = query.getSingleResult();
		return results;
	}
	

	@Override // все пройденые маршруты водителя по id
	public Integer getAcomplishRoutesByDriverId(Long id) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
	    CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
	    Root<Route> root = criteria.from(Route.class);
	    criteria.select(root);   		
		criteria.where(cBuilder.and(cBuilder.equal(root.get(Route_.active), false),
				(cBuilder.equal(root.get(Route_.driver), id))));	
	    TypedQuery<Route> query = getEm().createQuery(criteria);
	    // плохой вариант но я не нашел меторда который посчитает
	    // к-во рядов в запросе
		return query.getResultList().size();
	}

	public Root<Route> prepareRoot(Root<Route> root){
	    root.fetch(Route_.driver).fetch(Driver_.user);
	    root.fetch(Route_.car).fetch(Car_.carType);
	    root.fetch(Route_.userOrder).fetch(UserOrder_.user); 
	    root.fetch(Route_.userOrder).fetch(UserOrder_.cargoBeginningCity);
	    root.fetch(Route_.userOrder).fetch(UserOrder_.cargoFinalCity);
	    return root;
	}

	@Override
	public List<Route> getAllDriver(SingularAttribute<Route, ?> attr,
			boolean ascending, int startRecord, int pageSize, Long DriverId) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
		Root<Route> root = criteria.from(Route.class);

		criteria.select(root);
		root = prepareRoot(root);
		criteria.orderBy(new OrderImpl(root.get(attr), ascending));
		//criteria.where(cBuilder.and(cBuilder.equal(root.get(Route_.active), true)));	
		if (DriverId!=null){
			// мы передаем Id  usera а ищем по id drivera
			criteria.where(cBuilder.and(cBuilder.equal(root.get(Route_.driver), DriverId)));			
		}
	    criteria.distinct(true);	

		TypedQuery<Route> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Route> results = query.getResultList();
		return results;
	}

	@Override // находит маршрут по UserOrder, если нет то возвращает null
	public Route getRouteByUserOrder(Long id) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
		Root<Route> root = criteria.from(Route.class);

		criteria.select(root);
		root = prepareRoot(root);
		
		criteria.where(cBuilder.and(cBuilder.equal(root.get(Route_.userOrder), id)));	
	    criteria.distinct(true);
	    TypedQuery<Route> query = getEm().createQuery(criteria);
	    
	    Route results;
	    
	    try{
	    	 results = query.getSingleResult();
	    }
	    // если результатов нет то передается значение null
	    catch(NoResultException exp){
	    	return null;
	    }
	    
		return results;
	}


	@Override
	public List<Route> getRouteByAtr(SingularAttribute<Route, ?> attr, Long id) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Route> criteria = cBuilder.createQuery(Route.class);
		Root<Route> root = criteria.from(Route.class);

		criteria.select(root);
		root = prepareRoot(root);
		criteria.where(cBuilder.and(cBuilder.equal(root.get(attr), id)));	
	    criteria.distinct(true);	

		TypedQuery<Route> query = getEm().createQuery(criteria);

		List<Route> results = query.getResultList();
		return results;
	}	
	
	

}
